package org.shop.search.service.iml;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang.ArrayUtils;

import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.shop.common.pojo.Result;
import org.shop.goods.feign.SkuFeign;
import org.shop.goods.pojo.Sku;
import org.shop.search.dao.SkuMapper;
import org.shop.search.pojo.SkuInfo;
import org.shop.search.service.SkuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class SkuServiceIml implements SkuService {
    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private SkuFeign skuFeign;


    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public void importSkuData() {

        Sku sku = new Sku();
        sku.setStatus("1");
        int page = 1;
        int rows = 1000;
        do {
            Result<PageInfo> result = skuFeign.search(sku, page, rows);
            PageInfo<Sku> pageInfo = result.getData();

            List<Sku> skus = pageInfo.getList();
            if (skus.size() == 0) {
                break;
            }

            List<SkuInfo> skuInfos = JSON.parseArray(JSON.toJSONString(skus), SkuInfo.class);
            // 将spec转成specMap
            skuInfos.forEach(skuInfo -> {
                Map<String, Object> map = JSON.parseObject(skuInfo.getSpec(), Map.class);
                skuInfo.setSpecMap(map);
            });
            skuMapper.saveAll(skuInfos);

            page++;
            rows = skus.size();
        } while (rows == 1000);

    }

    /**
     * brand 品牌
     * category 分类
     * spec
     * keywords，输入的条件
     */
    @Override
    public Map search(Map<String, String> paramMap) throws Exception {

        Map<String, Object> resultMap = new HashMap<>();
        NativeSearchQueryBuilder nativeSearchQueryBuilder = buildBasicQuery(paramMap);
        Map<String, Object> groupSearchMap = getGroupSearchMap(nativeSearchQueryBuilder, paramMap);

        //2.搜索关键字设置高亮
        HighlightBuilder.Field field = new HighlightBuilder.Field("name"); //指定高亮域
        //前缀 <em style='color:red;'>
        field.preTags("<em style='color:red;'>").postTags("</em>").fragmentSize(100); //碎片长度,关键词的长度
        nativeSearchQueryBuilder.withHighlightFields(field);
        //3.执行查询
        AggregatedPage<SkuInfo> skuPage = elasticsearchTemplate.queryForPage(
                nativeSearchQueryBuilder.build(), //搜索条件
                SkuInfo.class,                    //数据集合要转换的类型
                new SearchResultMapper() { //执行搜索后将结果集封装到该对象中

                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                        List<T> content = new ArrayList<>();
                        //获取所有数据,高亮和非高亮
                        for (SearchHit hit : searchResponse.getHits()) {
                            //获取非高亮数据
                            SkuInfo skuInfo = JSON.parseObject(hit.getSourceAsString(), SkuInfo.class);
                            //获取高亮数据
                            HighlightField highlightField = hit.getHighlightFields().get("name");
                            if (highlightField != null && highlightField.getFragments() != null) {
                                //取出高亮数据
                                Text[] fragments = highlightField.getFragments();
                                //StringBuilder线程不安全 StringBuffer线程安全
                                StringBuilder sb = new StringBuilder();
                                for (Text fragment : fragments) {
                                    sb.append(fragment.toString());
                                }
                                //高亮替换非高亮
                                skuInfo.setName(sb.toString());
                            }
                            content.add((T) skuInfo);
                        }
                        //1)搜索的集合数据:(携带高亮)List<T> content
                        //2)分页对象:Pageable pageable
                        //3)搜索记录的总条数:long total
                        return new AggregatedPageImpl<>(content, pageable, searchResponse.getHits().getTotalHits(), searchResponse.getScrollId());
                    }
                });
        //4.返回结果
        resultMap.put("rows", skuPage.getContent());
        resultMap.put("total", skuPage.getTotalElements());
        resultMap.put("totalPages", skuPage.getTotalPages());
        resultMap.putAll(groupSearchMap);

        //获取分页参数给前端分页使用
        Pageable pageable = nativeSearchQueryBuilder.build().getPageable();
        resultMap.put("pageNum", pageable.getPageNumber()); //当前页
        resultMap.put("pageSize", pageable.getPageSize()); //每页显示多少条
        return resultMap;

    }

    private NativeSearchQueryBuilder buildBasicQuery(Map<String, String> paramMap) {
        //创建查询对象
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (!CollectionUtils.isEmpty(paramMap)) {

            String keywords = paramMap.get("keywords");
            if (!StringUtils.isEmpty(keywords)) {
                boolQueryBuilder.must(QueryBuilders.matchQuery("name", keywords));
            }
            //品牌
            String brand = paramMap.get("brand");
            if (!StringUtils.isEmpty(brand)) {
                boolQueryBuilder.must(QueryBuilders.termQuery("brandName", brand));
            }
            //分类
            String category = paramMap.get("category");
            if (!StringUtils.isEmpty(category)) {
                boolQueryBuilder.must(QueryBuilders.termQuery("categoryName", category));
            }
            for (String key : paramMap.keySet()) {
                //以spec_开头的表示规格参数
                if (key.startsWith("spec_")) {
                    String value = paramMap.get(key).replace("\\", "");
                    //在es中保存的类型是specMap.xxx.keyword
                    boolQueryBuilder.must(QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", value));
                }
            }
            String price = paramMap.get("price");
            if (!StringUtils.isEmpty(price)) {
                price = price.replace("元", "").replace("以上", "");
                //分割
                String[] prices = price.split("-");
                if (ArrayUtils.isNotEmpty(prices)) {
                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(Integer.parseInt(prices[0]))); //大于
                    if (price.length() == 2) {
                        boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(Integer.parseInt(prices[1]))); //小于等于
                    }
                }
            }
            //排序
            String sort = paramMap.get("sort"); //排序域(字段)
            String order = paramMap.get("order"); //排序规则
            if (!StringUtils.isEmpty(order) && !StringUtils.isEmpty(sort)) {
                nativeSearchQueryBuilder.withSort(new FieldSortBuilder(sort).order(SortOrder.valueOf(order.toUpperCase())));
            }

        }
        //分页
        Integer page = getPage(paramMap);
        Integer size = 50;
        nativeSearchQueryBuilder.withPageable(PageRequest.of(page - 1, size));

        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        return nativeSearchQueryBuilder;
    }

    private Integer getPage(Map<String, String> paramMap) {
        try {
            if (!StringUtils.isEmpty(paramMap.get("pageNum"))) {
                return Integer.parseInt(paramMap.get("pageNum"));
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        return 1;
    }

    /**
     * 获取分组列表数据
     */
    private Map<String, Object> getGroupSearchMap(NativeSearchQueryBuilder nativeSearchQueryBuilder, Map<String, String> searchMap) {
        Map<String, Object> resultMap = new HashMap<>();
        //设置查询的条件
        if (CollectionUtils.isEmpty(searchMap) || StringUtils.isEmpty(searchMap.get("category"))) {
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName").size(1000));
        }
        if (CollectionUtils.isEmpty(searchMap) || StringUtils.isEmpty(searchMap.get("brand"))) {
            nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName").size(1000));
        }
        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("specAgg").field("spec.keyword").size(1000));

        //执行查询
        AggregatedPage<SkuInfo> skuPage = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class);

        //获取聚合结果
        if (CollectionUtils.isEmpty(searchMap) || StringUtils.isEmpty(searchMap.get("category"))) {
            StringTerms categoryNameAgg = (StringTerms) skuPage.getAggregation("categoryNameAgg");
            List<String> categoryList = getResultFromStringTerms(categoryNameAgg);
            resultMap.put("categoryList", categoryList);
        }
        if (CollectionUtils.isEmpty(searchMap) || StringUtils.isEmpty(searchMap.get("brand"))) {
            StringTerms brandNameAgg = (StringTerms) skuPage.getAggregation("brandNameAgg");
            List<String> brandList = getResultFromStringTerms(brandNameAgg);
            resultMap.put("brandList", brandList);
        }

        StringTerms spec = (StringTerms) skuPage.getAggregation("specAgg");
        Map<String, Set<String>> specMap = getSpecMap(spec);
        resultMap.put("specMap", specMap);

        return resultMap;

    }

    private List<String> getResultFromStringTerms(StringTerms stringTerms) {
        List<String> groupSearchList = new ArrayList<>();
        if (stringTerms != null) {
            for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
                String fieldName = bucket.getKeyAsString();//分类的名称
                groupSearchList.add(fieldName);
            }
        }
        return groupSearchList;
    }

    /**
     * 获取规格列表数据
     */
    private Map<String, Set<String>> getSpecMap(StringTerms spec) {

        /*
         1.设置查询的条件
		nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("specAgg").field("spec.keyword").size(1000));
		//2.构建查询对象
		NativeSearchQuery query = nativeSearchQueryBuilder.build();
		//3.执行查询
		AggregatedPage<SkuInfo> skuPage = elasticsearchTemplate.queryForPage(query, SkuInfo.class);
		//4.获取分组结果
		StringTerms spec = (StringTerms) skuPage.getAggregation("specAgg");
		*/

        Map<String, Set<String>> specMap = new HashMap<>();
        //得到去重的spec字符串集合
        Set<String> specList = new HashSet<>();
        if (spec != null) {
            for (StringTerms.Bucket bucket : spec.getBuckets()) {
                specList.add(bucket.getKeyAsString());
            }
        }
        //将spec字符串集合转换成map
        for (String specjson : specList) {
            //{"手机屏幕尺寸":"5寸","网络":"联通2G","颜色":"红","测试":"s11","机身内存":"16G","存储":"16G","像素":"800万像素"}
            Map<String, String> map = JSON.parseObject(specjson, Map.class);
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String key = entry.getKey();        //规格名字
                String value = entry.getValue();    //规格选项值
                //获取当前规格名字对应的规格数据
                Set<String> specValues = specMap.get(key);
                if (CollectionUtils.isEmpty(specValues)) {
                    specValues = new HashSet<>();
                }
                //将当前规格加入到集合中
                specValues.add(value);
                //将数据存入到specMap中
                specMap.put(key, specValues);
            }
        }
        return specMap;
    }

}
